﻿@inject IActionService _actionService
@inject IModelCatalogService _modelCatalogService
@inject ChatState _chatState
@inject ISnackbar Snackbar

<EditForm Model="@Action" OnValidSubmit="OnValidSubmit">
    <DataAnnotationsValidator />
    <div class="d-flex flex-column flex-grow-1 gap-3 pa-4">
        <MudText>Name</MudText>
        <MudTextField  @bind-Value="Action!.Name" 
                       Variant="Variant.Outlined" 
                       Margin="Margin.Dense" 
                       For="@(() => Action!.Name)" 
                       HelperText="Use a short name that clearly describes what this action represents." />

        <MudText>Category</MudText>
        <MudSelect @bind-Value="Action.Category"
                   Margin="Margin.Dense"
                   T="string"
                   Variant="Variant.Outlined"
                   For="@(() => Action!.Category)"
                   HelperText="Used to group actions displayed in the main actions view.">

            @foreach (var category in Categories)
            {
                <MudSelectItem Value="@category" />
            }
        </MudSelect>

        <MudText>Icon</MudText>
        <IconSelector @bind-SelectedIcon="Action.Icon" />
        <HelperText Text="Icons help to identify actions. Select one from the available choices by clicking the button." />

        <MudText>Model</MudText>
        <MudSelect  @bind-Value="Action.Model" 
                    Margin="Margin.Dense" 
                    T="string" 
                    Variant="Variant.Outlined" 
                    For="@(() => Action!.Model)" 
                    HelperText="The model that will be used when executing actions.">
            
            @foreach (var model in ModelCatalog.Models)
            {
                <MudSelectItem Value="@model.Name" />
            }
        </MudSelect>

        <MudText>Metaprompt</MudText>
        <MudTextField @bind-Value="Action!.Metaprompt"
                      T="string" Variant="Variant.Outlined"
                      AutoGrow
                      Lines="7"
                      MaxLines="10"
                      Margin="Margin.Dense"
                      For="@(() => Action!.Metaprompt)" />
        
        <div class="px-2">
            <MudText Typo="Typo.caption" Class="mt-0" Style="color: var(--mud-palette-text-secondary); font-size: .75rem; text-align: start; font-weight: 400; line-height: 1.66; letter-spacing: .03333em;">
                @((MarkupString)metapromptGuidelines)
            </MudText>
        </div>

        <MudText>Single Message Mode</MudText>
        <div class="d-flex flex-grow-1">
            <HelperText Text="@singleMessageModeGuidelines" />
            <MudSwitch @bind-Value="@Action.IsSingleMessageMode" Color="Color.Primary" Class="my-auto" Style="margin-left: auto; margin-right: -4px;" />
        </div>

        <MudText>Remote action</MudText>
        <div class="d-flex flex-grow-1">
            <HelperText Text="@remoteActionGuidelines" />
            <MudSwitch @bind-Value="@Action.IsRemoteAction" Color="Color.Primary" Class="my-auto" Style="margin-left: auto; margin-right: -4px;" />
        </div>

        @if (Action.IsRemoteAction)
        {
            <MudText>Webhook URL</MudText>
            <div class="d-flex">
                <MudTextField @bind-Value="Action.Webhook!.Url"
                                InputType="@(showWebhookUrl ? InputType.Text : InputType.Password)"
                                Variant="Variant.Outlined"
                                Margin="Margin.Dense"
                                For="@(() => Action.Webhook!.Url)" />
                <MudIconButton Icon="@(showWebhookUrl ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility)" OnClick="ToggleWebhookUrlVisibility" Edge="Edge.End" Class="ml-1" />
            </div>
            <div class="px-2">
                <MudText Typo="Typo.caption" Class="mt-0" Style="color: var(--mud-palette-text-secondary); font-size: .75rem; text-align: start; font-weight: 400; line-height: 1.66; letter-spacing: .03333em;">
                    @((MarkupString)webhookUrlGuidelines)
                </MudText>
            </div>
            
            <MudText>Authorization Token (Optional)</MudText>
            <div class="d-flex align-items-center">
                <MudTextField   @bind-Value="Action.Webhook!.Headers" 
                                InputType="@(showAuthorizationToken ? InputType.Text : InputType.Password)"
                                Variant="Variant.Outlined" 
                                Margin="Margin.Dense" 
                                For="@(() => Action.Webhook!.Headers)" 
                                HelperText="Provide the API key for secure access to external services."
                                Disabled="true"/>
                <MudIconButton Icon="@(showAuthorizationToken ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility)" OnClick="ToggleAuthorizationTokenVisibility" Edge="Edge.End" Class="ml-1 mb-5" />
            </div>
        }
    </div>
    <div class="d-flex flex-row flex-grow-1 justify-end gap-4 pa-4">
        <MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto">Save</MudButton>
    </div>
</EditForm>

@code 
{
    [Parameter] public Guid ActiontId { get; set; }
    [Parameter] public EventCallback OnActionSubmit { get; set; }
    private NativeActionResponse Action { get; set; } = new();
    private ModelCatalogResponse ModelCatalog { get; set; } = new();

    public List<string> Categories { get; set; } = new()
    {
        "AI",
        "Audio",
        "Automation Scenarios",
        "Business",
        "Chatbots",
        "Coding",
        "Content Creation",
        "Copywriting",
        "Critical Thinking",
        "Customer Support",
        "Data Analysis",
        "Data Visualization",
        "DevOps",
        "E-commerce",
        "External API",
        "Finance",
        "Fun",
        "Homelab",
        "Human Resources",
        "Image Generation",
        "Image Vision",
        "Integration",
        "Low Code",
        "Marketing",
        "No Code",
        "Notifications",
        "Productivity",
        "Project Management",
        "Remote Action",
        "Sales",
        "SEO",
        "Social Media",
        "Text",
        "Video",
        "Webhooks",
        "Workflow Automation",
        "Other"
    };

    private bool coerceText;
    private bool coerceValue;

    private bool showWebhookUrl = false;
    private bool showAuthorizationToken = false;
    private void ToggleWebhookUrlVisibility() => showWebhookUrl = !showWebhookUrl;
    private void ToggleAuthorizationTokenVisibility() => showAuthorizationToken = !showAuthorizationToken;

    private const string webhookUrlGuidelines = @"
Provide the URL for the webhook endpoint. Ensure the response returns a JSON object.
Example: <span style='color: var(--mud-palette-primary);'><strong>{ ""status"": ""success"", ""data"": { ""key"": ""value"" } }</strong></span>.";

    private const string metapromptGuidelines = @"
A set of instructions defining what this action should perform. 
If 'Remote Action' is selected, ensure to specify 'return JSON object' in the instructions.
Example: <span style='color: var(--mud-palette-primary);'><strong>{ ""status"": ""success"", ""data"": { ""key"": ""value"" } }</strong></span>.<br /><br />
<strong>Available placeholders:</strong><br />
- <span style='color: var(--mud-palette-primary);'><strong>{DATE}</strong></span> will be replaced with local current date";

    private const string remoteActionGuidelines = @"
Enabling this option allows connection to external services, enabling you to trigger automation scenarios 
or integrate with various APIs and webhooks.";

    private const string singleMessageModeGuidelines = @"
Enabling this option sends only the user's last message to the action instead of the entire chat history.

Some actions require just the latest message, while others need the full conversation context. Enable as needed.";

    private bool success;

    protected override async Task OnInitializedAsync()
    {
        await LoadModelCatalog();
    }

    protected override async Task OnParametersSetAsync()
    {
        await PopulateForm();
    }

    private async Task LoadModelCatalog()
    {
        var response = await _modelCatalogService.BrowseModelsCatalog();
        var modelCatalog = response.Value;

        if (!response.Succeeded)
        {
            ShowSnackbar("Failed to load model catalog. Please try again.", Severity.Error);
        }

        if (modelCatalog is not null)
        {
            ModelCatalog = modelCatalog;
        }
    }

    private async void OnValidSubmit(EditContext context)
    {
        if (ActiontId != Guid.Empty)
        {
            await UpdateActiontAsync(Action!);
        }
        else
        {
            await CreateActionAsync();
        }

        success = true;
        await OnActionSubmit.InvokeAsync();
        StateHasChanged();
    }

    private async Task PopulateForm()
    {
        if (Guid.Empty != ActiontId)
        {
            await FetchActionToUpdate();
        }
        else
        {
            SetupNewActionParameters();
        }
    }

    private async Task FetchActionToUpdate()
    {
        var response = await _actionService.GetAction(ActiontId);
        var action = response.Value;

        if (action is not null)
        {
            Action = new NativeActionResponse
            {
                Id = action.Id,
                Name = action.Name,
                Category = action.Category,
                Icon = action.Icon,
                Model = action.Model,
                Metaprompt = action.Metaprompt,
                IsSingleMessageMode = action.IsSingleMessageMode,
                IsRemoteAction = action.IsRemoteAction,
                ShouldRephraseResponse = action.ShouldRephraseResponse,
                Webhook = new WebhookResponse
                {
                    HttpMethod = action.Webhook!.HttpMethod,
                    Url = action.Webhook!.Url,
                    Payload = action.Webhook.Payload,
                    IsRetryEnabled = action.Webhook.IsRetryEnabled,
                    RetryCount = action.Webhook.RetryCount,
                    RetryInterval = action.Webhook.RetryInterval,
                    IsScheduled = action.Webhook.IsScheduled,
                    CronExpression = action.Webhook.CronExpression,
                    Headers = action.Webhook.Headers
                }
            };
        }
    }

    private void SetupNewActionParameters()
    {
        Action.IsSingleMessageMode = false;
        Action.ShouldRephraseResponse = false;
        Action.Webhook!.HttpMethod = "POST";
        Action.Webhook!.Payload = string.Empty;
        Action.Webhook!.IsRetryEnabled = false;
        Action.Webhook!.RetryCount = 3;
        Action.Webhook!.RetryInterval = 10;
        Action.Webhook!.IsScheduled = false;
        Action.Webhook!.CronExpression = string.Empty;
        Action.Webhook!.Headers = [];
    }

    private async Task CreateActionAsync()
    {
        if (Action is not null)
        {
            var response = await _actionService.CreateAction(Action);

            if(!response.Succeeded)
            {
                ShowSnackbar("Failed to create action. Please try again later.", Severity.Error);
                return;
            }

            _chatState.NotifyActionsRefreshed();
            ShowSnackbar("Action created successfully.", Severity.Success);
        }
    }

    private async Task UpdateActiontAsync(NativeActionResponse action)
    {
        if (action is not null)
        {
            var response = await _actionService.UpdateAction(action);

            if (!response.Succeeded)
            {
                ShowSnackbar("Failed to update action. Please try again later.", Severity.Error);
                return;
            }

            _chatState.NotifyActionsRefreshed();
            ShowSnackbar("Action updated successfully.", Severity.Success);
        }
    }

    private async Task<IEnumerable<string>?> SearchAsync(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return Categories;
        }

        var result = Categories
            .Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase));

        return await Task.FromResult(result);
    }

    private void OnValueChanged(string value)
    {
        Action.Category = value.IsEmpty() ? string.Empty : value;
    }

    private void ShowSnackbar(string message, Severity severity)
    {
        Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopRight;

        Snackbar.Add(message, severity, options =>
        {
            options.HideTransitionDuration = 100;
        });
    }
}
